/*
 * Copyright (C) 2010 TopCoder Inc., All Rights Reserved.
 */
package com.hercules.android.receiver;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.View.OnTouchListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RelativeLayout;
import android.widget.VideoView;
import android.widget.RelativeLayout.LayoutParams;

import com.hercules.android.receiver.commands.AggregateXRECommandHandler;
import com.hercules.android.receiver.commands.XRECommandHandler;
import com.hercules.android.receiver.commands.XREConnectCommandHandler;
import com.hercules.android.receiver.commands.XRENewCommandHandler;
import com.hercules.android.receiver.commands.XRERestartCommandHandler;
import com.hercules.android.receiver.commands.XRESetCommandHandler;
import com.hercules.android.receiver.commands.XREShutdownCommandHandler;
import com.hercules.android.receiver.commands.call.XREAnimateCallCommandHandler;
import com.hercules.android.receiver.commands.call.XRECallCommandHandler;
import com.hercules.android.receiver.commands.call.animate.XREHandlerAlphaAnimate;
import com.hercules.android.receiver.commands.call.animate.XREHandlerAnimate;
import com.hercules.android.receiver.commands.call.animate.XREHandlerTransformAnimate;
import com.hercules.android.receiver.entities.ApplicationConnectionData;
import com.hercules.android.receiver.entities.DeviceCapabilities;
import com.hercules.android.receiver.entities.XREAlphaAnimation;
import com.hercules.android.receiver.entities.XREApplicationData;
import com.hercules.android.receiver.entities.XREFont;
import com.hercules.android.receiver.entities.XREImage;
import com.hercules.android.receiver.entities.XREObject;
import com.hercules.android.receiver.entities.XRERectangle;
import com.hercules.android.receiver.entities.XREText;
import com.hercules.android.receiver.entities.XRETransformAnimation;
import com.hercules.android.receiver.entities.XREVideo;
import com.hercules.android.receiver.entities.XREView;
import com.hercules.android.receiver.painter.XREImagePainter;
import com.hercules.android.receiver.painter.XREPainter;
import com.hercules.android.receiver.painter.XRERectanglePainter;
import com.hercules.android.receiver.painter.XRETextPainter;
import com.hercules.android.receiver.painter.XREVideoPainter;
import com.hercules.android.receiver.painter.XREViewPainter;

/**
 * <p>
 * This is the entry <code>Activity</code> of the Android Receiver application.
 * </p>
 * 
 * @author TCSASSEMBER
 * @version 1.0
 */
public class Main extends Activity {
    /**
     * Represents the <code>Receiver</code> instance.
     */
    private Receiver receiver;

    /**
     * Create the <code>Receiver</code> instance.
     */
    private void setUpReceiver() {
        // create receiver instance
        receiver = new Receiver();

        // create DeviceCapabilities instance
        DeviceCapabilities deviceCapabilities = new DeviceCapabilities();
        deviceCapabilities.setPlatform(Config.PLATFORM);
        deviceCapabilities.setPlatform(Config.PLATFORM_VERSION);
        deviceCapabilities.setReceiverVersion(Config.RECEIVER_VERSION);
        WindowManager windowManager = getWindowManager();
        Display display = windowManager.getDefaultDisplay();
        deviceCapabilities.setNativeDimensions(new Integer[] { display.getWidth(), display.getHeight() });

        // create XREApplicationData instance
        XREApplicationData applicationData = new XREApplicationData();
        applicationData.setCurrentCommandIndex(0);
        applicationData.setSessionGUID(Config.SESSION_GUID);

        // create object painters
        Map<String, XREPainter> objectPainters = new HashMap<String, XREPainter>();
        objectPainters.put(Config.XRE_KLASS_VIEW, new XREViewPainter(receiver));
        objectPainters.put(Config.XRE_KLASS_RECTANGLE, new XRERectanglePainter(receiver));
        objectPainters.put(Config.XRE_KLASS_TEXT, new XRETextPainter(receiver));
        objectPainters.put(Config.XRE_KLASS_IMAGE, new XREImagePainter(receiver));
        objectPainters.put(Config.XRE_KLASS_VIDEO, new XREVideoPainter(receiver));
        applicationData.setObjectPainters(objectPainters);
        receiver.setApplicationData(applicationData);

        // crate connection data
        ApplicationConnectionData connectionData = new ApplicationConnectionData();
        connectionData.setDeviceCaps(deviceCapabilities);
        connectionData.setMinimumVersion(Config.MINIMUM_VERSION);
        receiver.setApplicationConnectionData(connectionData);

        // set up mappings from klass type to XRE object type
        Map<String, Class<? extends XREObject>> klassObjects = new HashMap<String, Class<? extends XREObject>>();
        klassObjects.put(Config.XRE_KLASS_VIEW, XREView.class);
        klassObjects.put(Config.XRE_KLASS_FONT, XREFont.class);
        klassObjects.put(Config.XRE_KLASS_IMAGE, XREImage.class);
        klassObjects.put(Config.XRE_KLASS_RECTANGLE, XRERectangle.class);
        klassObjects.put(Config.XRE_KLASS_TEXT, XREText.class);
        klassObjects.put(Config.XRE_KLASS_VIDEO, XREVideo.class);
        klassObjects.put(Config.XRE_KLASS_ALPHA_ANIMATION, XREAlphaAnimation.class);
        klassObjects.put(Config.XRE_KLASS_TRANSFORM_ANIMATION, XRETransformAnimation.class);

        // set up command handler
        Map<String, XRECommandHandler> handlers = new HashMap<String, XRECommandHandler>();
        handlers.put(Config.XRE_COMMAND_CONNECT, new XREConnectCommandHandler(receiver));
        handlers.put(Config.XRE_COMMAND_RESTART, new XRERestartCommandHandler(receiver));
        handlers.put(Config.XRE_COMMAND_SET, new XRESetCommandHandler(receiver));
        handlers.put(Config.XRE_COMMAND_NEW, new XRENewCommandHandler(receiver, klassObjects));
        handlers.put(Config.XRE_COMMAND_SHUTDOWN, new XREShutdownCommandHandler(receiver));
        // set up call command handler
        Map<String, XREHandlerAnimate> animateHandlers = new HashMap<String, XREHandlerAnimate>();
        // set up animate handlers
        animateHandlers.put(Config.XRE_KLASS_ALPHA_ANIMATION, new XREHandlerAlphaAnimate());
        animateHandlers.put(Config.XRE_KLASS_TRANSFORM_ANIMATION, new XREHandlerTransformAnimate());
        XREAnimateCallCommandHandler animateCallHandler = new XREAnimateCallCommandHandler(receiver, animateHandlers);
        Map<String, XRECommandHandler> callHandlers = new HashMap<String, XRECommandHandler>();
        callHandlers.put(Config.XRE_ANIMATE_METHOD, animateCallHandler);
        handlers.put(Config.XRE_COMMAND_CALL, new XRECallCommandHandler(receiver, callHandlers));
        receiver.setCommandHandler(new AggregateXRECommandHandler(receiver, handlers));
    }

    /**
     * Called when the activity is first created.
     * 
     * @param savedInstanceState
     *            the saved instance state
     */
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // No Title bar and full screen
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        requestWindowFeature(Window.FEATURE_NO_TITLE);

        setUpFirstView();
    }

    /**
     * Set up the first connection information view.
     */
    private void setUpFirstView() {
        // show input view to let user input XRE server address
        setContentView(R.layout.input);

        final Button btn = (Button) findViewById(R.id.connect_btn);
        final EditText hostEdit = (EditText) findViewById(R.id.host_edit);
        final EditText portEdit = (EditText) findViewById(R.id.port_edit);
        final EditText applicationNameEdit = (EditText) findViewById(R.id.applicationName_edit);
        final Activity activity = this;
        hostEdit.requestFocus();

        btn.setOnClickListener(new View.OnClickListener() {
            /**
             * The handler of the click event.
             * 
             * @param targetView
             *            the target view which caused the event
             */
            public void onClick(View targetView) {
                ((InputMethodManager) getSystemService(INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(activity
                        .getCurrentFocus().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
                setUpReceiver();

                // create the root view
                MainView view = new MainView(activity, receiver);
                int port = 0;
                try {
                    port = Integer.parseInt(portEdit.getText().toString());
                } catch (NumberFormatException e) {
                    // should not happen
                }
                receiver.getApplicationConnectionData().setHost(hostEdit.getText().toString());
                receiver.getApplicationConnectionData().setPort(port);
                receiver.getApplicationConnectionData().setApplicationName(applicationNameEdit.getText().toString());
                try {
                    // create connection manager
                    ConnectionManager connectionManager = new ConnectionManager(receiver,
                            hostEdit.getText().toString(), port);
                    receiver.setConnectionManager(connectionManager);
                } catch (Exception e) {
                    Log.v(Config.APPLICATION_NAME, "Error occurs when connecting to server:", e);
                    showErrorDialog("Error occurs when connecting to server:" + e.getMessage());
                    return;
                }

                final RelativeLayout layout = new RelativeLayout(activity);
                LayoutParams lp = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
                lp.addRule(RelativeLayout.ALIGN_PARENT_TOP);
                lp.addRule(RelativeLayout.ALIGN_LEFT);
                layout.addView(view, lp);

                layout.setOnTouchListener(new OnTouchListener() {
                    /**
                     * Called when a touch event is dispatched to a view.
                     * 
                     * @param v
                     *            The view the touch event has been dispatched to.
                     * @param event
                     *            The MotionEvent object containing full information about the event.
                     */
                    public boolean onTouch(View v, MotionEvent event) {
                        StringBuilder sb = new StringBuilder();
                        sb.append("{\"source\":").append(Config.ROOTVIEW_ID).append(",\"handler\":").append(
                                Config.ROOTVIEW_ID).append(",\"timestamp\":");
                        sb.append(new Date().getTime());
                        sb.append(",\"params\":{\"x\":").append((int) event.getX()).append(",\"y\":").append(
                                (int) event.getY());
                        sb.append("},\"name\":\"onMouseDown\"}");
                        try {
                            receiver.getConnectionManager().sendEvent(sb.toString());
                        } catch (ConnectionManagerException e) {
                            return false;
                        }
                        return true;
                    }
                });
                receiver.setAppRootView(layout);
                receiver.getApplicationData().setRootViewUpdateHandler(new Handler() {
                    /**
                     * Handle the message. It just invalidate the layout view.
                     * 
                     * @param msg
                     *            the <code>Message</code> instance
                     */
                    public void handleMessage(Message msg) {
                        super.handleMessage(msg);
                        layout.invalidate();
                    }
                });
                receiver.getApplicationData().setLayoutRefreshHandler(new Handler() {
                    /**
                     * Handle the message. It will remove all video instances.
                     * 
                     * @param msg
                     *            the <code>Message</code> instance
                     */
                    public void handleMessage(Message msg) {
                        super.handleMessage(msg);
                        for (VideoView videoView : receiver.getApplicationData().getVideos()) {
                            videoView.stopPlayback();
                            layout.removeView(videoView);
                        }
                        receiver.getApplicationData().setVideos(new ArrayList<VideoView>());
                    }
                });
                receiver.getApplicationData().setShowDialogHandler(new Handler() {
                    /**
                     * Handle the message. It will call showErrorDialog method to show an error message.
                     * 
                     * @param msg
                     *            the <code>Message</code> instance
                     */
                    public void handleMessage(Message msg) {
                        super.handleMessage(msg);
                        String message = msg.getData().getString(Config.ERROR_MESSAGE_KEY);
                        showErrorDialog(message);
                    }
                });
                receiver.getApplicationData().setShowConnectionViewHandler(new Handler() {
                    /**
                     * Handle the message. It will show the connection view.
                     * 
                     * @param msg
                     *            the <code>Message</code> instance
                     */
                    public void handleMessage(Message msg) {
                        activity.setContentView(R.layout.input);
                        setUpFirstView();
                    }
                });

                activity.setContentView(layout);
                // send onConnect event to XRE server
                try {
                    receiver.getConnectionManager().connect();
                } catch (ConnectionManagerException e) {
                    Log.v(Config.APPLICATION_NAME, "Error occurs when sending onConnect event.", e);
                    showErrorDialog("Error occurs when sending onConnect event:" + e.getMessage());
                    return;
                }
                receiver.getConnectionManager().startSocketThread();
            }
        });
    }

    /**
     * Show an error dialog with the provided error message.
     * 
     * @param message
     *            the error message
     */
    private void showErrorDialog(String message) {
        Dialog dialog = new AlertDialog.Builder(this).setIcon(R.drawable.icon).setTitle("Error").setMessage(message)
                .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                    /**
                     * The handler for button's click event.
                     * 
                     * @param dialog
                     *            The dialog that received the click.
                     * @param which
                     *            The button that was clicked or the position of the item clicked.
                     */
                    public void onClick(DialogInterface dialog, int which) {
                        if (receiver != null && receiver.getConnectionManager() != null) {
                            receiver.getConnectionManager().close();
                        }
                        Main.this.setContentView(R.layout.input);
                        setUpFirstView();
                    }
                }).create();
        dialog.show();
    }

    /**
     * Called when this activity is to destroy.
     */
    public void onDestroy() {
        if (receiver != null && receiver.getConnectionManager() != null) {
            receiver.getConnectionManager().close();
        }
        super.onDestroy();
    }
}
